package com.zxy.supplier_system.server.repository

import com.zxy.supplier_system.server.controller.MarketController
import com.zxy.supplier_system.server.entity.*
import com.zxy.supplier_system.server.utils.SpecificationUtils
import com.zxy.supplier_system.server.utils.joinOnce
import jakarta.persistence.criteria.CriteriaBuilder
import jakarta.persistence.criteria.CriteriaQuery
import jakarta.persistence.criteria.From
import jakarta.persistence.criteria.Predicate
import org.springframework.data.jpa.domain.Specification
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.JpaSpecificationExecutor
import java.util.*

interface MarketRepository : JpaRepository<Market, Int>, JpaSpecificationExecutor<Market> {

    fun findAllBySystemService(systemService: SystemService): List<Market>

    fun findByIdAndSystemService(id: Int, systemService: SystemService): Market?

    fun findByShoppingGuidesContainsOrSaleManagerEqualAndId(employee: Employee, marketId: Int): Market? {
        val findBy = this.findBy<Market, Optional<Market>>(

            Specification.allOf(
                Specifications.ifUCanSee(employee),
                Specifications.idEqual(marketId),
            ),
        ) { q ->
            q.one()
        }
        return findBy.orElse(null)
    }

    object Specifications {

        fun ifICanSee(
            from: From<*, Market>,
            criteriaBuilder: CriteriaBuilder,
            query: CriteriaQuery<*>,
            employee: Employee
        ): Predicate {
            if (employee.authorities.contains(Authority.卖场_查询所有)) {
                return criteriaBuilder.conjunction()
            }
            return criteriaBuilder.or(
                saleManagerEqual(from, criteriaBuilder, employee.id!!),
                shoppingGuidesContains(from, criteriaBuilder, query,employee.id!!)
            )
        }

        fun ifUCanSee(employee: Employee): Specification<Market> {
            return (if (employee.authorities.contains(Authority.卖场_查询所有)) {
                Specification.where(null)
            } else {
                saleManagerEqual(employee.id!!)
                    .or(shoppingGuidesContains(employee.id!!))
            })
        }

        fun idEqual(marketId: Int?): Specification<Market> {
            return Specification { root, _, criteriaBuilder ->
                marketId?.let {
                    criteriaBuilder.equal(root.get<Int>("id"), it)
                }
            }
        }

        fun fromQueryMarket(
            queryMarketsRequest: MarketController.QueryMarketsRequest
        ): Specification<Market> {
            return Specification.allOf(nameContains(queryMarketsRequest.name))
        }

        fun shoppingGuidesContains(employeeId: Int): Specification<Market> {
            return Specification { root, query, criteriaBuilder ->
                shoppingGuidesContains(root,criteriaBuilder,query,employeeId)
            }
        }

        fun shoppingGuidesContains(
            from: From<*, Market>,
            criteriaBuilder: CriteriaBuilder,
            query: CriteriaQuery<*>,
            employeeId: Int
        ): Predicate {
            val subquery = query.subquery(Int::class.java)
            val subqueryRoot = subquery.from(MarketShoppingGuide::class.java)
            subquery.where(
                criteriaBuilder.and(
                    criteriaBuilder.equal(
                        subqueryRoot.joinOnce<MarketShoppingGuide, Employee>("employee").get<Int>("id"),
                        employeeId
                    ),
                    criteriaBuilder.equal(
                        from.get<Int>("id"),
                        subqueryRoot.joinOnce<MarketShoppingGuide, Employee>("market").get<Int>("id")
                    ),
                )
            )

            return criteriaBuilder.exists(subquery)
        }

        fun saleManagerEqual(employeeId: Int): Specification<Market> {

            return Specification { root, _, criteriaBuilder ->
                saleManagerEqual(root, criteriaBuilder, employeeId)
            }
        }

        fun saleManagerEqual(
            from: From<*, Market>,
            criteriaBuilder: CriteriaBuilder,
            employeeId: Int
        ): Predicate {
            return criteriaBuilder.equal(
                from.joinOnce<Market, Employee>("saleManager").get<Int>("id"),
                employeeId
            )
        }

        fun nameContains(name: String?): Specification<Market> {
            return SpecificationUtils.contains({
                it.get("name")
            }, name)
        }

        fun haveCheckInventoryRecord(): Specification<Market> {
            return Specification { root, _, criteriaBuilder ->
                criteriaBuilder.isNotEmpty(root.get("checkInventoryRecords"))
            }
        }

        fun systemServiceEquals(currentSystemService: SystemService): Specification<Market> {
            return SystemServiceRepository.Specifications.systemServiceEquals({ root -> root }, currentSystemService)
        }

    }

}
